#include "QF_OLED.h"

#if OLED_LIB_EN

#include "QF_OLED_FONT.h"

typedef struct
{
    uint8_t area_x_start;
    uint8_t area_x_stop;
    uint8_t area_y_start;
    uint8_t area_y_stop;
} oled_display_area_t;

static uint8_t qf_oled_buffer[PAGE_SIZE][WIDTH];
static uint8_t oled_x_offset = 0;
static oled_display_area_t area;
static int16_t _position_x = 0, _position_y = 0;
static int16_t _position_font_x = 0;
static uint8_t _font_size = 1;
static uint8_t _auto_line = auto_line_disable;

static uint8_t _x_space = 0;
static uint8_t _y_space = 0;
static uint8_t cuser_flg = 1;
static uint8_t _chinese_size = 0;

static uint8_t _width = WIDTH;
static uint8_t _height = HEIGHT;
static uint8_t _page_size = PAGE_SIZE;

static char chinese_buffer[4] = {0, 0, 0, 0};
static uint8_t chinese_buffer_count = 0;

typedef enum
{
    oled_write_cmd,
    oled_write_data = 0x40
} oled_write_type;

static void chinese_size_scan()
{
    _chinese_size = sizeof(BMP_CH_FONT_1616) / sizeof(BMP_CH_FONT_1616_t);
}

void oled_write_byte(uint8_t mode, uint8_t dat)
{
#if OLED_WRITE_MODE
    oled_iic_write_bytes(oled_slave_addr, mode, &dat, 1);
#else
    CS_CLR;
    if (mode)
        DC_SET;
    else
        DC_CLR;
    oled_spi_write_bytes(&dat, 1);
    DC_SET;
    CS_SET;
#endif
}

void oled_begin()
{
#if (OLED_WRITE_MODE == 0)
    RES_CLR;
#endif
    chinese_size_scan();
    area.area_x_start = 0;
    area.area_y_start = 0;
    area.area_x_stop = WIDTH;
    area.area_y_stop = HEIGHT;
#if (OLED_WRITE_MODE == 0)
    RES_SET;
#endif
    oled_write_byte(oled_write_cmd, 0xAE);
#ifdef SSD1306_12864
    {
        /**************初始化SSD1306*****************/

        oled_write_byte(oled_write_cmd, 0x00); //---set low column address
        oled_write_byte(oled_write_cmd, 0x10); //---set high column address
        oled_write_byte(oled_write_cmd, 0x40); //--set start line address
        oled_write_byte(oled_write_cmd, 0xB0); //--set page address

        oled_write_byte(oled_write_cmd, 0xA1); //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
        oled_write_byte(oled_write_cmd, 0xC8); // Set COM/Row Scan Direction   0xc0上下反置 0xc8正常

        oled_write_byte(oled_write_cmd, 0xA8); //--set multiplex ratio(1 to 64)
        oled_write_byte(oled_write_cmd, 0x3F); //--1/32 duty
                                               //    oled_write_byte(oled_write_cmd, 0xC8,OLED_CMD);//Com scan direction
        oled_write_byte(oled_write_cmd, 0xD3); //-set display offset
        oled_write_byte(oled_write_cmd, 0x00); //

        oled_write_byte(oled_write_cmd, 0xD5); // set osc division
        oled_write_byte(oled_write_cmd, 0x80); //

        oled_write_byte(oled_write_cmd, 0xD8); // set area color mode off
        oled_write_byte(oled_write_cmd, 0x05); //

        oled_write_byte(oled_write_cmd, 0xD9); // Set Pre-Charge Period
        oled_write_byte(oled_write_cmd, 0xF1); //

        oled_write_byte(oled_write_cmd, 0xDA); // set com pin configuartion
        oled_write_byte(oled_write_cmd, 0x12); //

        oled_write_byte(oled_write_cmd, 0xDB); // set Vcomh
        oled_write_byte(oled_write_cmd, 0x30); //

        oled_write_byte(oled_write_cmd, 0x8D); // set charge pump enable
        oled_write_byte(oled_write_cmd, 0x14); //

        /*******************************************/
    }
#elif defined SH1106_12864
    {

        /**************初始化CH1106***************/
        oled_write_byte(oled_write_cmd, 0x02); //---set low column address
        oled_write_byte(oled_write_cmd, 0x10); //---set high column address
        oled_write_byte(oled_write_cmd, 0x40); //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
        oled_write_byte(oled_write_cmd, 0xB0);
        oled_write_byte(oled_write_cmd, 0xA1); //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
                                               //    oled_write_byte(oled_write_cmd, 0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
        oled_write_byte(oled_write_cmd, 0xA6); //--set normal display
        oled_write_byte(oled_write_cmd, 0xA8); //--set multiplex ratio(1 to 64)
        oled_write_byte(oled_write_cmd, 0x3f); //--1/64 duty
        oled_write_byte(oled_write_cmd, 0xAD);
        oled_write_byte(oled_write_cmd, 0x8B);
        oled_write_byte(oled_write_cmd, 0x33);
        oled_write_byte(oled_write_cmd, 0xC8);
        oled_write_byte(oled_write_cmd, 0xD3); //-set display offset Shift Mapping RAM Counter (0x00~0x3F)
        oled_write_byte(oled_write_cmd, 0x00); //-not offset
        oled_write_byte(oled_write_cmd, 0xd5); //--set display clock divide ratio/oscillator frequency
        oled_write_byte(oled_write_cmd, 0x80); //--set divide ratio, Set Clock as 100 Frames/Sec
        oled_write_byte(oled_write_cmd, 0xD9); //--set pre-charge period
        oled_write_byte(oled_write_cmd, 0x1F); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
        oled_write_byte(oled_write_cmd, 0xDA); //--set com pins hardware configuration
        oled_write_byte(oled_write_cmd, 0x12);
        oled_write_byte(oled_write_cmd, 0xDB); //--set vcomh
        oled_write_byte(oled_write_cmd, 0x40); // Set VCOM Deselect Level
                                               /*******************************************/
        oled_x_offset = 2;
    }
#elif defined SSD1306_12832
    {
        oled_write_byte(oled_write_cmd, 0xAE);
        oled_write_byte(oled_write_cmd, 0x40); //---set low column address
        oled_write_byte(oled_write_cmd, 0xB0); //---set high column address
        oled_write_byte(oled_write_cmd, 0xC8); //-not offset
        oled_write_byte(oled_write_cmd, 0xa1); // 段重定向设置
        oled_write_byte(oled_write_cmd, 0xa6); //
        oled_write_byte(oled_write_cmd, 0xa8); // 设置驱动路数
        oled_write_byte(oled_write_cmd, 0x1f);
        oled_write_byte(oled_write_cmd, 0xd3);
        oled_write_byte(oled_write_cmd, 0x00);
        oled_write_byte(oled_write_cmd, 0xd5);
        oled_write_byte(oled_write_cmd, 0xf0);
        oled_write_byte(oled_write_cmd, 0xd9);
        oled_write_byte(oled_write_cmd, 0x22);
        oled_write_byte(oled_write_cmd, 0xda);
        oled_write_byte(oled_write_cmd, 0x02);
        oled_write_byte(oled_write_cmd, 0xdb);
        oled_write_byte(oled_write_cmd, 0x49);
        oled_write_byte(oled_write_cmd, 0x8d);
        oled_write_byte(oled_write_cmd, 0x14);
    }
#endif

    oled_write_byte(oled_write_cmd, 0x81);       // contract control
    oled_write_byte(oled_write_cmd, Brightness); //--128
    oled_write_byte(oled_write_cmd, 0xaf);

    oled_fill_full_screen(0);
    oled_push_screen();
}

void oled_set_light(uint8_t val)
{
    oled_write_byte(oled_write_cmd, 0x81); // contract control
    oled_write_byte(oled_write_cmd, val);  //--128
}

void oled_set_rotation(oled_display_type mode)
{
    if (mode == inverse)
    {
        oled_write_byte(oled_write_cmd, 0xA0); //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
        oled_write_byte(oled_write_cmd, 0xC0); // Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    }
    else
    {
        oled_write_byte(oled_write_cmd, 0xA1); //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
        oled_write_byte(oled_write_cmd, 0xC8); // Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    }
}

void oled_set_power(uint8_t state)
{
    if (state)
    {
        oled_write_byte(oled_write_cmd, 0xaf);
    }
    else
    {
        oled_write_byte(oled_write_cmd, 0xAE);
    }
}

void oled_set_pos(uint8_t x, uint8_t y)
{
    x += oled_x_offset;
    oled_write_byte(oled_write_cmd, YLevel + y); // 设置页地址（0~7）
    oled_write_byte(oled_write_cmd, (x >> 4) | XLevelH);
    oled_write_byte(oled_write_cmd, (x & 0x0f));
}

void oled_push_screen()
{
    uint8_t i;
    for (i = 0; i < _page_size; i++)
    {
        oled_set_pos(0, i);
#if OLED_WRITE_MODE
        oled_iic_write_bytes(oled_slave_addr, oled_write_data, &qf_oled_buffer[i][0], _width);
#else
        CS_CLR;
        oled_spi_write_bytes(&qf_oled_buffer[i][0], _width);
        CS_SET;
#endif
    }
}

void oled_fill_full_screen(uint8_t val)
{
    uint8_t i, j;
    for (i = 0; i < PAGE_SIZE; i++)
    {
        for (j = 0; j < WIDTH; j++)
        {
            qf_oled_buffer[i][j] = val;
        }
    }
}

void oled_draw_pix(int16_t x, int16_t y, uint8_t mode)
{
    static uint8_t tmp;
    if (y >= area.area_y_stop || y < area.area_y_start)
        return;
    if (x >= area.area_x_stop || x < area.area_x_start)
        return;
    tmp = y >> 3;
    y -= tmp << 3;
    if (mode == 1)
        qf_oled_buffer[tmp][x] |= 1 << y;
    else
        qf_oled_buffer[tmp][x] &= ~(1 << y);
}

void oled_draw_line(int16_t x, int16_t y, uint8_t width, uint8_t height, uint8_t mode)
{
    static uint8_t i, j, k, l, tmp, n;

    if (x >= area.area_x_stop)
        return;
    if (y >= area.area_y_stop)
        return;

    if (x < area.area_x_start)
    {
        if ((x + width) < area.area_x_start)
            return;
        width -= (int16_t)area.area_x_start - x;
        x = area.area_x_start;
    }
    if (y < area.area_y_start)
    {
        if ((y + height) < area.area_y_start)
            return;
        height -= (int16_t)area.area_y_start - y;
        y = area.area_y_start;
    }
    if ((x + width) > area.area_x_stop)
    {
        width = area.area_x_stop - x;
    }
    if ((y + height) > area.area_y_stop)
    {
        height = area.area_y_stop - y;
    }
    l = y;

    if (mode == 1)
    {
        for (i = 0; i < height; i++)
        {
            k = x;
            tmp = l >> 3;
            n = 1 << (l - (tmp << 3));
            for (j = 0; j < width; j++)
                qf_oled_buffer[tmp][k++] |= n;
            l++;
        }
    }
    else
    {
        for (i = 0; i < height; i++)
        {
            k = x;
            tmp = l >> 3;
            n = ~(1 << (l - (tmp << 3)));
            for (j = 0; j < width; j++)
                qf_oled_buffer[tmp][k++] &= n;
            l++;
        }
    }
}

void oled_fill_box(int16_t start_x, int16_t start_y, int16_t stop_x, int16_t stop_y, uint8_t mode)
{
    oled_draw_line(start_x, start_y, stop_x - start_x, stop_y - start_y, mode);
}

void oled_set_area(uint8_t start_x, uint8_t start_y, uint8_t stop_x, uint8_t stop_y)
{
    if (start_x >= _width)
        area.area_x_start = _width - 1;
    else
        area.area_x_start = start_x;

    if (stop_x > _width)
        area.area_x_stop = _width;
    else
        area.area_x_stop = stop_x;

    if (start_y >= _height)
        area.area_y_start = _height - 1;
    else
        area.area_y_start = start_y;

    if (stop_y > _height)
        area.area_y_stop = _height;
    else
        area.area_y_stop = stop_y;
}

void oled_set_area_size(uint8_t start_x, uint8_t start_y, uint8_t width, uint8_t height)
{
    oled_set_area(start_x, start_y, width + start_x, start_y + height);
}

void oled_set_position(int16_t x, int16_t y)
{
    _position_x = x;
    _position_font_x = x;
    _position_y = y;
    cuser_flg = 1;
}

void oled_set_font_size(uint8_t size)
{
    if (size)
        _font_size = size;
    else
        _font_size = 1;
    cuser_flg = 1;
}

static void draw_bmp(uint8_t width, uint8_t height, uint8_t *p, uint8_t _size, uint8_t mode)
{
    static uint8_t i, j, k, tmp;
    static int16_t _tmp_x, tmp_y;

    if (_auto_line == auto_line_enable)
    {

        if (_position_x > area.area_x_stop - (_size * width) - _x_space)
        {
            if (_position_y < area.area_y_stop - (_size * width) - _y_space)
            {
                _position_x = _position_font_x;
                if (height == 1)
                    _position_y += (_size << 3) + _y_space;
                else
                    _position_y += (_size << 4) + _y_space;
            }
            else
            {
                cuser_flg = 0;
                return;
            }
        }
    }
    else if (_position_x >= area.area_x_stop || _position_y >= area.area_y_stop)
    {
        cuser_flg = 0;
        return;
    }
    for (i = 0; i < height; i++)
    {
        _tmp_x = _position_x;
        for (j = 0; j < width; j++)
        {
            tmp_y = _position_y;
            tmp = *p++;
            for (k = 0; k < 8; k++)
            {
                if (tmp & 1)
                {
                    oled_draw_line(_tmp_x, tmp_y, _size, _size, mode);
                }
                tmp >>= 1;
                tmp_y += _size;
            }
            _tmp_x += _size;
        }
        _position_y += _size << 3;
    }
    _position_y -= (_size * height) << 3;
    _position_x += _size * width + _x_space;
}

static void oled_draw_chinese_one(const char *str, uint8_t mode)
{
    uint8_t i, *p = NULL, _size_;

    for (i = 0; i < _chinese_size; i++)
    {
        if (BMP_CH_FONT_1616[i].val_ch[0] == str[0])
        {
            if (BMP_CH_FONT_1616[i].val_ch[1] == str[1])
            {
                if (BMP_CH_FONT_1616[i].val_ch[2] == str[2])
                    p = (uint8_t *)BMP_CH_FONT_1616[i].val_bmp;
            }
        }
    }

    if (p == NULL)
        return;

    _size_ = _font_size >> 1;
    if (_size_ == 0)
        _size_ = 1;
    draw_bmp(16, 2, p, _size_, mode);
}

// void oled_draw_chinese(const char *str, uint8_t mode)
// {
//     while (*str && cuser_flg)
//     {
//         oled_draw_chinese_one(str, mode);
//         str += 3;
//     }
// }

void oled_draw_char(char chr, uint8_t mode)
{
    uint8_t width, height, *p = NULL, _size_tmp = _font_size;
    char tmp = chr;
    if ((uint8_t)chr >= 0x80)
    {
        chinese_buffer[chinese_buffer_count++] = chr;
        if (chinese_buffer_count == 3)
        {
            chinese_buffer_count = 0;
            oled_draw_chinese_one((const char *)chinese_buffer, mode);
        }
        return;
    }
    chinese_buffer_count = 0;
    chr -= ' ';
    switch (_size_tmp % 2)
    {
    case 0: // 偶数，采用8*16
        p = (uint8_t *)&font_ascii_8x16[(chr << 4)];
        width = 8;
        height = 2;
        _size_tmp >>= 1;
        break;
    case 1: // 奇数，采用6*8
        p = (uint8_t *)&font_ascii_6x8[(chr * 6)];
        width = 6;
        height = 1;
        break;
    default:
        break;
    }
    if (tmp == '\n')
    {
        if (_position_y < area.area_y_stop - (_size_tmp * width) - _y_space)
        {
            _position_x = _position_font_x;
            if (height == 1)
                _position_y += (_size_tmp << 3) + _y_space;
            else
                _position_y += (_size_tmp << 4) + _y_space;
            cuser_flg = 1;
        }
        else
        {
            _position_x = area.area_x_stop + 1;
            cuser_flg = 0;
            return;
        }
    }
    else
        draw_bmp(width, height, p, _size_tmp, mode);
}

void oled_set_auto_line(oled_display_type type)
{
    switch (type)
    {
    case auto_line_enable:
        _auto_line = auto_line_enable;
        break;
    default:
        _auto_line = auto_line_disable;
        break;
    }
}

void oled_draw_str(const char *str, uint8_t mode)
{
    while (*str)
        oled_draw_char(*str++, mode);
}

void oled_set_print_space(uint8_t x_space, uint8_t y_space)
{
    _x_space = x_space;
    _y_space = y_space;
}

void oled_draw_bmp(const uint8_t *bmp, uint8_t mode)
{
    uint8_t tmp, i;
    tmp = bmp[1];
    i = _auto_line;
    if (tmp % 8)
        tmp = (tmp >> 3) + 1;
    else
        tmp >>= 3;
    _auto_line = auto_line_disable;
    draw_bmp(bmp[0], tmp, bmp + 2, 1, mode);
    _auto_line = i;
}

void oled_push_area(int16_t x, int16_t y, uint8_t width, uint8_t height)
{
    uint8_t j, l;
    if (x >= _width)
        return;
    if (y >= _height)
        return;

    if (x < 0)
    {
        if ((x + width) < 0)
            return;
        width += x;
        x = 0;
    }
    if (y < 0)
    {
        if ((y + height) < 0)
            return;
        height += y;
        y = 0;
    }
    if ((x + width) > _width)
    {
        width = _width - x;
    }

    j = height;
    height = height >> 3;

    if (j % 8)
        height++;

    l = y % 8;

    if ((height << 3) < (j + l))
        height++;
    l = y >> 3;
    height += l;

    if (height > _page_size)
        height = _page_size;

    for (; l < height; l++)
    {
        oled_set_pos(x, l);
#if OLED_WRITE_MODE
        oled_iic_write_bytes(oled_slave_addr, oled_write_data, &qf_oled_buffer[l][x], width);
#else
        CS_CLR;
        oled_spi_write_bytes(&qf_oled_buffer[l][x], width);
        CS_SET;
#endif
    }
}

#endif
